
//
// 130417 - AH - Created.
// 130609 - AH - Changed related to XmlValidator may return parse result.
//

package org.japril.servlet.xmlservlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.japril.jutil.CharArrayPrintWriter;
import org.japril.servlet.WebAppUser;

/**
 * <p>
 *   Base class for 
 *   {@link HttpServlet}s that handles XML based requests.
 * </p>
 * <p>
 *   Override protected methods to provide production servlet, deployed this
 *   will simply make sure received XML parse as XML and response with XML 
 *   either being fail report or received XML.
 * </p>
 * @author Arne Halvorsen (AH)
 */
@SuppressWarnings("serial")
public class XmlRequestServlet extends HttpServlet
{
  private String m_para;
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public XmlRequestServlet(){ this("xml"); }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param para Name on parameter to pick up XML from. If {@code null} or the
   *             empty string will read XML text directly.
   */
  public XmlRequestServlet(String para)
  {
    para=(para==null) ? "" : para.trim();
    m_para=(para.length()==0) ? null : para;
  }
  
  /**
   * <p>
   *   Gets name on parameter to pick up XML text from.
   * </p>
   * @return Name on parameter or {@code null} if gets XML to directly.
   */
  public String getXmlParameterName(){ return m_para; }
  
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException 
  {
    perform(req, resp);
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException 
  {
    perform(req, resp);
  }
  
  /**
   * <p>
   *   Performs the request.
   * </p>
   * @param req {@link HttpServletRequest}.
   * @param res {@link HttpServletResponse}.
   * @throws IOException If fails.
   */
  private void perform(HttpServletRequest req, HttpServletResponse res)
    throws IOException
  {
    try
    {
      WebAppUser wau=authenticate(req, res);
    
      String xml=getXml(req);
      if (xml==null)
      {
        handleNoXml(req, res);
        return;
      }
      
      // Validates.
      XmlValidator val=newXmlValidator();
      //Object parsed=val.validate(xml);
      val.validate(xml);
      
      // Initialize environment.
      EnvironmentInitializer ei=newEnvironmentInitializer(wau);
      Object o=ei.initialize(xml);
      
      // Engages the engine.
      EngineHandler eh=ei.getEngineHandler();
      if (eh!=null)
      {
        //eh.perform(xml, parsed, o);
    	eh.perform(xml, o);  
      }
      
      // Responds.
      ResponseHandler rh=ei.getResponseHandler();
      if (rh!=null)
      {
        rh.perform(wau, xml, o, res);
      }
    }
    catch (IOException iox)
    {
      fail(iox, res);
    }
  }
  
  protected void fail(IOException iox, HttpServletResponse res)
    throws IOException
  {
    String msg=iox.getLocalizedMessage();
    msg=(msg==null) ? "" : msg.trim();
    
    res.setContentType("text/xml");
    PrintWriter pw=res.getWriter();
    
    pw.println("<?xml version='1.0'?>");
    pw.println("<fail>");
    pw.println(msg);
    pw.println("</fail>");
    pw.flush();
  }
  
  /**
   * <p>
   *   Authenticates.
   * </p>
   * <p>
   *   This returns {@code null} (no authentication needed), override to
   *   provide authentication.
   * </p>
   * @param req  Request.
   * @param resp Response.
   * @return Object that represents user, {@code null} if authentication not
   *         not needed.
   * @throws IOException If fails.
   */
  protected WebAppUser authenticate(HttpServletRequest req, 
    HttpServletResponse resp) throws IOException
  {
    return null;
  }
  
  /**
   * <p>
   *   Gets XML that defines the request.
   * </p>
   * <p>
   *   This simply lookup XML as the value of a request parameter named 'xml',
   *   override to get XML in other way.
   * </p>
   * @param req {@link HttpServletRequest}.
   * @return XML or {@code null} if not found, 
   *         {@link #handleNoXml(HttpServletResponse)} not found.
   */
  protected String getXml(HttpServletRequest req) throws IOException
  {
    if (m_para!=null)
    {
      String xml=req.getParameter("xml");
      return xml;
    }
    else
    {
      // Specified in XmlRequestServlet.
      InputStream is=null;
      try
      {
        CharArrayPrintWriter capw=new CharArrayPrintWriter();
        is=req.getInputStream();
        BufferedReader br=new BufferedReader(new InputStreamReader(is));
        String line=br.readLine();
        while (line!=null)
        {
          capw.println(line);
          line=br.readLine();
        }
        return capw.toString();
      }
      finally
      {
        if (is!=null) is.close();
      }
    }
  }
 
  /**
   * <p>
   *   Handles received no XML.
   * </p>
   * @param res {@link HttpServletResponse}.
   * @throws IOException If this is a failure.
   */
  protected void handleNoXml(HttpServletRequest req, 
    HttpServletResponse res) throws IOException
  {
    String uri=req.getRequestURI();
    
    res.setContentType("text/html");
    PrintWriter pw=res.getWriter();
    pw.println(
      "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"");
    pw.println("\"http://www.w3.org/TR/html4/loose.dtd\">");
    pw.println("<html><body>");
    pw.println("  <h2>Input XML</h2>");
    pw.println("  <form action='"+uri+"' method='post'>");
    pw.println("    <input type='submit' />");
    pw.println("    <textarea rows='40' cols='80' name='xml'>");
    pw.println("<?xml version='1.0'?><some-xml/>");
    pw.println("    </textarea>");
    pw.println("  </form>");
    pw.println("</body></html>");
    pw.println();
  }
  
  /**
   * <p>
   *   Creates
   *   {@link XmlValidator} to use.
   * </p>
   * <p>
   *   This creates 
   *   {@link DefaultXmlValidator}, override to create other type of
   *   {@code XmlValidator}.
   * </p>
   * @return Created.
   */
  protected XmlValidator newXmlValidator()
  {
    return new DefaultXmlValidator();
  }
  
  /**
   * <p>
   *   Gets 
   *   {@link EnvironmentInitializer} to use.
   * </p>
   * <p>
   *   Implementation will override, this provide prototype implementation
   *   causing server simply response with the XML received.
   * </p>
   * @param wau Represents end user doing request, may be {@code null}.
   * @return {@link EnvironmentInitializer} to use.
   */
  protected EnvironmentInitializer newEnvironmentInitializer(WebAppUser wau)
  {
    return new EnvironmentInitializer()
    {  
      public Object initialize(String xml) throws IOException
      {
        return null;
      }
      
      public ResponseHandler getResponseHandler() 
      {
        return new ResponseHandler()
        {
          public void perform(WebAppUser wau, String xml, Object o, 
            HttpServletResponse resp) throws IOException 
          {
            resp.setContentType("text/xml");
            PrintWriter pw=resp.getWriter();
            pw.println(xml);
            pw.flush();
          }
        };
      }

      public EngineHandler getEngineHandler(){ return null; }
      
    };
  }
  
}
